package io;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class SerialCommunicator extends IOClient implements SerialPortEventListener {

	
	private static Map<String, SerialCommunicator> comms = new TreeMap<String, SerialCommunicator>();
	private SerialPort serialConnection;
	private DataOutputStream output;
	private InputStream input;
	
	CommPortIdentifier identifier;

	public static SerialCommunicator getCommunicator(String port) {
		try {
			SerialCommunicator comm = comms.get(port);
			if(comm == null)
			{
				comm = new SerialCommunicator(port);
				comms.put(port, comm);
			}
			return comm;
		} catch (NoSuchPortException e) {
			System.out.println("No port could be found matching: " + port);
			return null;
		}
	}

	private SerialCommunicator(String port) throws NoSuchPortException {
		super(port);
		identifier = CommPortIdentifier.getPortIdentifier(port);
	}

	
	public boolean connect() {
			try {
				serialConnection = (SerialPort) identifier.open("eelinux.java",
						2000);
				serialConnection.setSerialPortParams(9600, SerialPort.DATABITS_8, 
	                    SerialPort.STOPBITS_1, 
	                    SerialPort.PARITY_NONE);
				serialConnection.notifyOnDataAvailable(true);
				serialConnection.addEventListener(this);
				serialConnection.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN & SerialPort.FLOWCONTROL_RTSCTS_OUT);
				return true;
			} catch (Exception e) {
				return false;
			}
	}

	public boolean disconnect()
	{
		serialConnection.close();
		return true;
	}
	
	public boolean isConnected() {
		return output != null || input != null;
	}

	
	public byte[] receive() {
		

		try {
			input = serialConnection.getInputStream();
		} catch (Exception e) {
			return null;
		}

		StringBuilder response = new StringBuilder();
		try {
			// read data
			int c = -1;
			while ((c = input.read()) != -1) {
				response.append((char) c);
			}

			input.close();
			// print data
		} catch (Exception e) {
			return new byte[0];
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (Exception e) {
				}
			}
		}

		return response.toString().getBytes();

	}

	public boolean send(File f)
	{
		try{
		BufferedReader read = new BufferedReader(new FileReader(f));
		
		StringBuilder build = new StringBuilder();
		String line = "";
		while((line = read.readLine()) != null)
			build.append(line);
		
		read.close();
		
		return send(build.toString().getBytes());
		}catch(Exception e){
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean send(byte[] data) {
		try {

			if(!isConnected())
				connect();
			
			output = new DataOutputStream(serialConnection.getOutputStream());
		
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		try {
			for(byte b : data)
			{
				output.write(b);
			}
			output.write('\n');
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			try {
				output.close();
			} catch (Exception e) {
			}
		}
	}

	public void alertListeners(SerialPortEvent event)
	{
		for(CommunicationListener l : getListeners())
		{
			l.communicationRequested(event);
		}
	}

	
	public void serialEvent(SerialPortEvent arg0) {
		 switch (arg0.getEventType()) {
	      case SerialPortEvent.BI:
	      case SerialPortEvent.OE:
	      case SerialPortEvent.FE:
	      case SerialPortEvent.PE:
	      case SerialPortEvent.CD:
	      case SerialPortEvent.CTS:
	      case SerialPortEvent.DSR:
	      case SerialPortEvent.RI:
	      case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
	         break;
	      case SerialPortEvent.DATA_AVAILABLE:
	    	  alertListeners(arg0);
		 }
	}
}
